package com.woniu.product.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.woniu.exception.YuJiaException;
import com.woniu.exception.orderInfo.OrderInfoException;
import com.woniu.exception.venueAdmin.VenueAdminException;
import com.woniu.exception.venueClassroom.VenueClassroomException;
import com.woniu.exception.venueInfo.VenueInfoException;
import com.woniu.exception.venueInfo.VenueInfoExceptionCode;
import com.woniu.model.Result;
import com.woniu.product.client.AccountClient;
import com.woniu.product.client.VenueClient;
import com.woniu.product.client.model.GetVenueCoachFrom;
import com.woniu.product.client.model.VenueAdminDto;
import com.woniu.product.client.model.VenueClassroom;
import com.woniu.product.client.model.VenueInfo;
import com.woniu.product.dao.CurriculumDao;
import com.woniu.product.dto.classroomDto.ClassroomDto;
import com.woniu.product.dto.curriculum.CurriculumDto;
import com.woniu.product.dto.venueCoach.VenueCoachDto;
import com.woniu.product.model.Class;
import com.woniu.product.model.Curriculum;
import com.woniu.product.param.LoadListClassParam;
import com.woniu.product.param.ScheduleClassParam;
import com.woniu.product.param.curriculum.AddCurriculumParam;
import com.woniu.product.param.curriculum.LoadCurriculumParam;
import com.woniu.product.service.ClassService;
import com.woniu.product.service.CurriculumService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.util.RedisKeyUtil;
import com.woniu.util.StateUtil;
import io.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 杜岷芫
 * @since 2024年07月12日
 */
@Service
public class CurriculumServiceImpl extends ServiceImpl<CurriculumDao, Curriculum> implements CurriculumService {
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private CurriculumDao curriculumDao;
    @Autowired
    private AccountClient accountClient;
    @Autowired
    private VenueClient venueClient;
    @Autowired
    private ClassService classService;

    @Override
    @GlobalTransactional(rollbackFor = Throwable.class)
    public void scheduleClass(ScheduleClassParam param) throws Exception {
        RLock fairLock = redissonClient.getFairLock(RedisKeyUtil.scheduleClassIdLock(param.getClassId()));

        RLock lock = redissonClient.getMultiLock(fairLock);
        try {
            boolean success = lock.tryLock(20, TimeUnit.SECONDS);
            if (success) {
                Curriculum curriculum = new Curriculum();
                curriculum.setClassId(param.getClassId());
                curriculum.setClassroomId(param.getClassroomId());
                curriculum.setStartTime(param.getStartTime());
                curriculum.setEndTime(param.getEndTime());
                curriculum.setCoachId(param.getCoachId());
                curriculum.setVenueId(param.getVenueId());
                curriculum.setCurriculumState(param.getCurriculumState());
                curriculum.setRemark(param.getRemark());


                List<Curriculum> list = curriculumDao.selectList(new QueryWrapper<Curriculum>().eq("class_id", param.getClassId()));


                if (LocalDateTimeUtil.parse(param.getStartTime()).isBefore(LocalDateTime.now())) {
                    throw new OrderInfoException(3503, "只能排班未来的时间");
                }

                if (LocalDateTimeUtil.parse(param.getStartTime()).isAfter(LocalDateTimeUtil.parse(param.getEndTime()))) {
                    throw new OrderInfoException(3504, "开始时间不能晚于结束时间");
                }
                curriculumDao.insert(curriculum);
            } else {
                throw new RuntimeException("服务器繁忙");
            }
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            lock.unlock();//释放锁
        }
    }

    /**
     * 根据场馆id查询已排班的团课
     *
     * @param param
     * @return
     */

    @Override
    public List<Curriculum> loadListClass(LoadListClassParam param) {
        QueryWrapper<Curriculum> wrapper = new QueryWrapper<>();
        wrapper.gt("start_time", param.getStartTime())
                .lt("start_time", param.getEndTime())
                .eq("venue_id", param.getVenueId());
        wrapper.or().gt("end_time", param.getStartTime());
        List<Curriculum> curricula = curriculumDao.selectList(wrapper);
        if (curricula.size() == 0) {
            throw new OrderInfoException(500, "该时间段无课程");
        }
        return curricula;
    }

    /**
     * 教练点击确认完课按钮完成团课
     *
     * @param coachId
     * @param classId
     */
    @Override
    public void notarizeClass(Integer coachId, Integer classId) {
        QueryWrapper<Curriculum> wrapper = new QueryWrapper<>();
        wrapper.eq("class_id", classId);
        Curriculum curriculum = curriculumDao.selectOne(wrapper);
        if (curriculum == null) {
            throw new OrderInfoException(3111, "不存在该团课排班信息");
        }
        if (!curriculum.getCoachId().equals(coachId)) {
            throw new OrderInfoException(3112, "不是该团课教练，无权处理");
        }
        if (LocalDateTime.now().isBefore(LocalDateTimeUtil.parse(curriculum.getEndTime()))) {
            throw new OrderInfoException(3113, "还未到结束时间，请勿提前结束");
        }
        if (!curriculum.getCurriculumState().equals(1)) {
            throw new OrderInfoException(3114, "课程状态异常");
        }
        curriculum.setCurriculumState(3);
        curriculumDao.updateById(curriculum);
    }

    /**
     * 根据课程id查询排班信息
     *
     * @param curriculumId
     * @return
     * @throws Exception
     */

    @Override
    public List<Curriculum> loadByClassId(Integer curriculumId) throws Exception {
        QueryWrapper<Curriculum> wrapper = new QueryWrapper<>();
        wrapper.eq("curriculum_id", curriculumId);
        wrapper.eq("curriculum_state", 1);
        List<Curriculum> curricula = curriculumDao.selectList(wrapper);


        return curricula;
    }

    @Override
    public PageInfo<CurriculumDto> loadCurriculum(LoadCurriculumParam param) {
        PageHelper.startPage(param.getPageNum() == null ? 1 : param.getPageNum(), param.getPageSize() == null ? 6 : param.getPageSize());
        List<CurriculumDto> curriculumDtos = curriculumDao.load(param);
        PageInfo<CurriculumDto> curriculumDtoPageInfo = new PageInfo<>(curriculumDtos);
        List<CurriculumDto> list = curriculumDtoPageInfo.getList();

        Set<Integer> classroomIds = list.stream().map(o -> o.getClassroomId()).collect(Collectors.toSet());
        Set<Integer> coachIds = list.stream().map(o -> o.getCoachId()).collect(Collectors.toSet());
        if (classroomIds != null && classroomIds.size() != 0) {
            GetVenueCoachFrom from = new GetVenueCoachFrom();
            from.setIds(coachIds);
            Result<List<VenueCoachDto>> venueCoachResult = accountClient.getVenueCoachBtIds(from);
            if (venueCoachResult.getCode() != 200) {
                throw new VenueAdminException(venueCoachResult.getCode(), venueCoachResult.getMsg());
            }
            List<VenueCoachDto> venueCoachs = venueCoachResult.getData();
            Map<Integer, VenueCoachDto> coachMap = new HashMap<>();
            for (VenueCoachDto venueCoach : venueCoachs) {
                coachMap.put(venueCoach.getVenueAdminId(), venueCoach);
            }
            for (CurriculumDto curriculumDto : list) {
                curriculumDto.setVenueCoach(coachMap.get(curriculumDto.getCoachId()));
            }
        }

        if (classroomIds != null && classroomIds.size() != 0) {
            Result<List<ClassroomDto>> venueRoomsResult = venueClient.loadVenueRooms(classroomIds);
            if (venueRoomsResult.getCode() != 200) {
                throw new VenueClassroomException(venueRoomsResult.getCode(), venueRoomsResult.getMsg());
            }
            List<ClassroomDto> classrooms = venueRoomsResult.getData();
            HashMap<Integer, ClassroomDto> classroomMap = new HashMap<>();
            for (ClassroomDto classroom : classrooms) {
                classroomMap.put(classroom.getClassroomId(), classroom);
            }
            for (CurriculumDto curriculumDto : list) {
                curriculumDto.setClassroom(classroomMap.get(curriculumDto.getClassroomId()));
            }
        }
        curriculumDtoPageInfo.setList(list);
        return curriculumDtoPageInfo;
    }

    @Override
    @Transactional
    public void addCurriculums(List<AddCurriculumParam> params) {
        //检验场馆
        Result<VenueInfo> venueInfoResult = venueClient.loadVenueInfoById(params.get(0).getVenueId());
        if (venueInfoResult.getCode() != 200)
            throw new VenueInfoException(venueInfoResult.getCode(), venueInfoResult.getMsg());
        VenueInfo venueInfo = venueInfoResult.getData();
        if (ObjectUtil.isEmpty(venueInfo))
            throw new VenueInfoException(VenueInfoExceptionCode.VENUE_INFO_NOT_EXIST);
        if (venueInfo.getVenueState() != StateUtil.VENUE_ADMIN_STATE_NORMAL)
            throw new VenueInfoException(VenueInfoExceptionCode.VENUE_INFO_STATE_ERROR);
        //检验课程
        Set<Integer> classIds = params.stream().map(o -> o.getClassId()).collect(Collectors.toSet());
        List<Class> classes = classService.listByIds(classIds);
        HashMap<Integer, Class> classMap = new HashMap<>();
        for (Class aClass : classes) {
            classMap.put(aClass.getClassId(), aClass);
            if (aClass.getClassState() != StateUtil.CLASS_STATE_NOMAL)
                throw new YuJiaException(500, "存在课程状态异常");
        }
        if (classes == null || classes.size() != classIds.size())
            throw new YuJiaException(500, "有课程没有找到");
        //检验教练
        Set<Integer> coachIds = classes.stream().map(o -> o.getCoachId()).collect(Collectors.toSet());
        GetVenueCoachFrom from = new GetVenueCoachFrom();
        from.setIds(coachIds);
        Result<List<VenueCoachDto>> venueCoachBtIds = accountClient.getVenueCoachBtIds(from);
        if (venueCoachBtIds.getCode() != 200)
            throw new YuJiaException(venueCoachBtIds.getCode(), venueCoachBtIds.getMsg());
        List<VenueCoachDto> coachs = venueCoachBtIds.getData();
        if (coachs == null || coachs.size() != coachIds.size()) {
            throw new YuJiaException(500, "有教练不存在");
        }
        for (VenueCoachDto coach : coachs) {
            if (coach.getVenueAdminState() != StateUtil.VENUE_ADMIN_STATE_NORMAL)
                throw new YuJiaException(500, "有教练状态异常");
        }
        //检验教室
        Set<Integer> classroomIds = classes.stream().map(o -> o.getClassroomId()).collect(Collectors.toSet());
        Result<List<ClassroomDto>> classroomResult = venueClient.loadVenueRooms(classroomIds);
        if (classroomResult.getCode() != 200)
            throw new YuJiaException(classroomResult.getCode(), classroomResult.getMsg());
        List<ClassroomDto> classrooms = classroomResult.getData();
        if (classrooms == null || classrooms.size() != classroomIds.size())
            throw new YuJiaException(500, "有教室没找到");
        for (ClassroomDto classroom : classrooms) {
            if (classroom.getClassroomState() != StateUtil.VENUE_CLASS_ROOM_STATE_NORMAL)
                throw new YuJiaException(500, "有教室状态异常");
        }

        //检验是否时间冲突
        //查出数据库相关数据
        String[] s = params.get(0).getStartTime().split(" ");
        QueryWrapper<Curriculum> qw = new QueryWrapper<Curriculum>().eq("venue_id", params.get(0).getVenueId())
                .eq("curriculum_state", 1).in("coach_id", coachIds)
                .in("class_id", classIds).like("start_time", s[0]);
        List<Curriculum> curriculumList = curriculumDao.selectList(qw);

        List<Curriculum> newCurriculums = BeanUtil.copyToList(params, Curriculum.class);
        for (Curriculum newCurriculum : newCurriculums) {
            newCurriculum.setCoachId(classMap.get(newCurriculum.getClassId()).getCoachId());
            newCurriculum.setClassroomId(classMap.get(newCurriculum.getClassId()).getClassroomId());
            long minute = classMap.get(newCurriculum.getClassId()).getMinute().longValue();
            newCurriculum.setEndTime(LocalDateTimeUtil.parse(newCurriculum.getStartTime(), "yyyy-MM-dd hh:mm:ss")
                    .plusMinutes(minute).toString());
        }
        for (Curriculum newCurriculum : newCurriculums) {
            //
            LocalDateTime newStartTime = LocalDateTimeUtil.parse(newCurriculum.getStartTime(), "yyyy-MM-dd hh:mm:ss");
            LocalDateTime newEndTime = LocalDateTimeUtil.parse(newCurriculum.getEndTime(), "yyyy-MM-dd hh:mm:ss");

            for (Curriculum curriculum : curriculumList) {
                LocalDateTime startTime = LocalDateTimeUtil.parse(curriculum.getStartTime(), "yyyy-MM-dd hh:mm:ss");
                LocalDateTime endTime = LocalDateTimeUtil.parse(curriculum.getEndTime(), "yyyy-MM-dd hh:mm:ss");
                if (!(endTime.isBefore(newStartTime) || startTime.isAfter(newEndTime)))
                    throw new YuJiaException(500,"课表安排存在时间冲突");
            }
            curriculumList.add(newCurriculum);
        }
        saveBatch(newCurriculums);
    }
}
